﻿using IGeekFan.AspNetCore.Knife4jUI;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.OpenApi.Models;
using System.Text.Encodings.Web;
using System.Text.Json.Serialization;
using System.Text.Json;
using System.Text.Unicode;
using WomanMotion.WebApi.Infrastructure.Json;
using NLog;
using NLog.Web;
using LogDashboard;
using LogDashboard.Authorization.Filters;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Mvc;
using WomanMotion.WebApi.Infrastructure.UnifyResult;
using WomanMotion.WebApi.Api.Utility;
using WomanMotion.WebApi.Service.IService;
using WomanMotion.WebApi.Service.Service;
using LogDashboard.Repository;
using WomanMotion.WebApi.Repository.IRepository;
using WomanMotion.WebApi.Repository.Repository;
using Microsoft.Extensions.FileProviders;
using System.Xml.Linq;
using WomanMotion.WebApi.Model.Entitys;
using AutoMapper;
using static NewLife.Remoting.ApiHttpClient;
using NewLife.Redis.Core;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Newtonsoft.Json;
using WomanMotion.WebApi.Api.Filters;
using WomanMotion.WebApi.Identity.JWT;
using WomanMotion.WebApi.Infrastructure.ServiceProviderInstance;
using WomanMotion.WebApi.Api.Limit;
using AspNetCoreRateLimit;
using WomanMotion.WebApi.Api.Wechat;
using Hangfire;
using static SKIT.FlurlHttpClient.Wechat.Api.Events.AddWaybillEvent.Types;
using Hangfire.PostgreSql;
using Hangfire.Dashboard.BasicAuthorization;
using Hangfire.HttpJob;
using System.Globalization;
using WomanMotion.WebApi.Infrastructure.AppSettings;

//NLog配置
var logger = NLog.LogManager.Setup().LoadConfigurationFromFile("LogConfig/NLog.config").GetCurrentClassLogger();
//跨越配置名称
var corsname = "any";
var builder = WebApplication.CreateBuilder(args);
// 注入配置项
builder.Services.AddOptions();

#region 跨越配置一

builder.Services.AddCors(options =>
{
    options.AddPolicy(corsname, builder =>
    {
        builder.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
    });
});

#endregion 跨越配置一

// Add services to the container.

#region Jwt

//Jwt验证
builder.Services.AddJwtToken(builder.Configuration);
//builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
//    .AddJwtBearer(options =>
//    {
//        var tokenModel = builder.Configuration.GetSection("Jwt").Get<TokenModelJwt>();
//        var secretByte = Encoding.UTF8.GetBytes(tokenModel.Secret);
//        //是否开启https  默认tuer
//        options.RequireHttpsMetadata = false;
//        options.TokenValidationParameters = new TokenValidationParameters()
//        {
//            ValidateIssuer = true,
//            ValidIssuer = tokenModel.Issuer,

//            ValidateAudience = true,
//            ValidAudience = tokenModel.Audience,

//            ValidateLifetime = true,

//            IssuerSigningKey = new SymmetricSecurityKey(secretByte)
//        };
//        options.Events = new JwtBearerEvents
//        {
//            //当Token认证不通过
//            OnChallenge = context =>
//            {
//                //此处代码为终止.Net Core默认的返回类型和数据结果，这个很重要哦，必须
//                context.HandleResponse();

//                //自定义自己想要返回的数据结果，我这里要返回的是Json对象，通过引用Newtonsoft.Json库进行转换

//                //自定义返回的数据类型
//                context.Response.ContentType = "application/json";
//                ////自定义返回状态码，默认为401
//                context.Response.StatusCode = StatusCodes.Status401Unauthorized;
//                ////输出Json数据结果
//                context.Response.WriteAsync(JsonConvert.SerializeObject(ResultOutput.NotOk("Token认证不通过")));
//                return Task.FromResult(0);
//            },
//            //403 无权限访问
//            OnForbidden = context =>
//            {
//                context.Response.ContentType = "application/json";
//                ////自定义返回状态码，默认为401
//                context.Response.StatusCode = StatusCodes.Status401Unauthorized;
//                //context.Response.StatusCode = StatusCodes.Status401Unauthorized;
//                ////输出Json数据结果
//                context.Response.WriteAsync(JsonConvert.SerializeObject(ResultOutput.NotOk("无权限访问")));
//                return Task.FromResult(0);
//            }
//        };
//    });

#endregion Jwt

#region NLog配置

builder.Logging.ClearProviders();
builder.Logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information);
builder.Host.UseNLog();

#endregion NLog配置

#region 添加日志面板

//添加日志面板
builder.Services.AddLogDashboard(opt =>
{
    var username = builder.Configuration.GetSection("LogDashboardUI:username").Value;
    var password = builder.Configuration.GetSection("LogDashboardUI:passwd").Value;
    //添加面板授权
    opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter(username, password));
});

#endregion 添加日志面板

#region 添加AspNetCoreRateLimit限流服务

// 添加AspNetCoreRateLimit服务
builder.Services.AddRateLimit(builder.Configuration);

#endregion 添加AspNetCoreRateLimit限流服务

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
//builder.Services.AddSwaggerGen();

#region 添加验证码组件

//添加验证码组件
// 默认使用内存存储（AddDistributedMemoryCache）
builder.Services.AddCaptcha(builder.Configuration, options =>
{
    // 自定义字体
    options.ImageOption.FontSize = 28;
    options.ImageOption.FontFamily = ResourceFontFamilysFinder.Find("Epilog"); // 字体的名字在打开ttf文件时会显示
});

#endregion 添加验证码组件

#region 用Knife4jUI替换掉SwaggerUI

//用Knife4jUI替换掉SwaggerUI 配置第一步
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "API V1", Version = "v1", Description = "女性运动", Contact = new OpenApiContact() { Name = "bf.w", Email = "495950454@qq.com" } });
    c.AddServer(new OpenApiServer()
    {
        Url = "",
        Description = "vvv"
    });
    c.CustomOperationIds(apiDesc =>
    {
        var controllerAction = apiDesc.ActionDescriptor as ControllerActionDescriptor;
        return controllerAction.ControllerName + "-" + controllerAction.ActionName;
    });
    //获取dot model注释文档
    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "WomanMotion.WebApi.Api.xml"), true);
    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "WomanMotion.WebApi.Model.xml"), true);
    c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "WomanMotion.WebApi.Infrastructure.xml"), true);
});

#endregion 用Knife4jUI替换掉SwaggerUI

#region JSON序列化辅助

builder.Services.AddControllers().AddJsonOptions(options =>
{
    //格式化日期时间格式
    options.JsonSerializerOptions.Converters.Add(new SystemTextJsonConvert.DatetimeJsonConverter());

    //忽略循环引用
    options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;

    //格式化输出
    options.JsonSerializerOptions.WriteIndented = true;

    //数据格式首字母小写
    options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;

    //取消Unicode编码
    options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);

    //数据格式原样输出
    //options.JsonSerializerOptions.PropertyNamingPolicy = null;

    //忽略空值
    //options.JsonSerializerOptions.IgnoreNullValues = false;

    //允许额外符号(逗号是指数组末尾的逗号，反序列化时遇到注释或逗号，会抛出异常，所以需配置AllowTrailingCommas，此项默认为false。)
    //options.JsonSerializerOptions.AllowTrailingCommas = false;

    //反序列化过程中属性名称是否使用不区分大小写的比较
    //options.JsonSerializerOptions.PropertyNameCaseInsensitive = false;
});

#endregion JSON序列化辅助

#region 注入AutoMapper

//注入AutoMapper
builder.Services.AddAutoMapper(typeof(MapperProfile));

#endregion 注入AutoMapper

#region Redis客户端

//默认读取配置文件：ConnectionStrings:Redis
builder.Services.AddNewLifeRedis();

#endregion Redis客户端

#region 注入数据库连接对象

//注入数据库连接对象
builder.Services.AddSqlsugarSetup(builder.Configuration);

#endregion 注入数据库连接对象

#region 批量自动注入接口和类

//批量注册依赖注入接口和类
builder.Services.AutoScrutor();
//手工注入数据库工厂
//builder.Services.AddTransient<IRoleRepository, RoleRepository>();
//手工注入业务类
//builder.Services.AddTransient<IUserCenterService, UserCenterService>();

#endregion 批量自动注入接口和类

#region 注入HttpContextAccessor

builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

#endregion 注入HttpContextAccessor

#region 模型绑定 特性验证，自定义返回格式

////模型绑定 特性验证，自定义返回格式
builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = actionContex =>
    {
        //获取验证失败的模型字段
        var errors = actionContex.ModelState
                    .Where(e => e.Value.Errors.Count > 0)
                    .Select(e => e.Value.Errors.First().ErrorMessage)
                    .ToList();
        var strErrorMsg = string.Join("|", errors);
        //设置返回内容
        return new BadRequestObjectResult(ResultOutput.NotOk(strErrorMsg))
        {
            StatusCode = StatusCodes.Status200OK
        };
    };
});

#endregion 模型绑定 特性验证，自定义返回格式

#region 注册全局过滤器

//builder.Services.AddScoped<AccessControlAttribute>();
builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<AuthorizationFilter>();
});

#endregion 注册全局过滤器

#region 注册微信组件

builder.Services.AddWechat(builder.Configuration);

#endregion 注册微信组件

#region Hangfire组件

// 设置当前线程的语言为中文
Thread.CurrentThread.CurrentUICulture = new CultureInfo("zh-CN");
builder.Services.AddHangfire(config =>
                config.UsePostgreSqlStorage(builder.Configuration.GetConnectionString("HangfireConnection")).UseHangfireHttpJob());
builder.Services.AddHangfireServer();

#endregion Hangfire组件

var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    //app.UseSwaggerUI();
    //SwaggerUI替换为UseKnife4UI 配置第二步
    app.UseKnife4UI(c =>
    {
        c.RoutePrefix = ""; // serve the UI at root
        c.SwaggerEndpoint("/v1/api-docs", "V1 Docs");
    });
    //app.UseMiddleware<GlobalExceptionMiddleware>();
}
else
{
    //自定义异常中间件
    app.UseMiddleware<GlobalExceptionMiddleware>();
}

#region PGSQL需要添加的配置

AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);

#endregion PGSQL需要添加的配置

#region Hangfire添加web面板

app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    Authorization = new[] {new BasicAuthAuthorizationFilter(new BasicAuthAuthorizationFilterOptions
    {
        RequireSsl = false,
        SslRedirect = false,
        LoginCaseSensitive = true,
        Users = new []
        {
            new BasicAuthAuthorizationUser
                    {
                        Login = "admin",
                        PasswordClear =  "wbflike2012"
                    }
        }
    })}
});

#endregion Hangfire添加web面板

#region 制定定时任务

var JobService = app.Services.GetRequiredService<ISendMessageService>();
// Recurring job
RecurringJob.AddOrUpdate("定时发送订阅消息", () => JobService.SendMsg(), "0 0/5 * * * ?");

#endregion 制定定时任务

#region 跨越配置二

app.UseCors(corsname);

#endregion 跨越配置二

#region 启用静态文件

//启用静态文件
var filepath = AppDomain.CurrentDomain.BaseDirectory + "wwwroot";
if (!Directory.Exists(filepath))//如果路径不存在
{
    Directory.CreateDirectory(filepath);//创建一个路径的文件夹
}
app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot")),
});

#endregion 启用静态文件

//启用路由
app.MapControllers();

// 添加AspNetCoreRateLimit限流中间件
app.UseIpRateLimiting();
//开启授权验证
app.UseAuthentication();//认证
app.UseAuthorization();//授权
//UseKnife4UI 配置第三步
app.MapSwagger("{documentName}/api-docs");
//日志面板中间件 访问路径 /logdashboard
app.UseLogDashboard();
//获取app对象存储到静态类
//ServiceProviderInstance.Instance = ((IApplicationBuilder)app).ApplicationServices;
//读取配置文件到静态类
AppSettingsHelper.Init(builder.Configuration);
//自定义系统初始化的一些设置
app.InItSystem();
app.Run();